home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-12-19 | 49.9 KB | 2,129 lines |
-
- /*
- * (a) (C) 1990 by Adobe Systems Incorporated. All rights reserved.
- *
- * (b) If this Sample Code is distributed as part of the Display PostScript
- * System Software Development Kit from Adobe Systems Incorporated,
- * then this copy is designated as Development Software and its use is
- * subject to the terms of the License Agreement attached to such Kit.
- *
- * (c) If this Sample Code is distributed independently, then the following
- * terms apply:
- *
- * (d) This file may be freely copied and redistributed as long as:
- * 1) Parts (a), (d), (e) and (f) continue to be included in the file,
- * 2) If the file has been modified in any way, a notice of such
- * modification is conspicuously indicated.
- *
- * (e) PostScript, Display PostScript, and Adobe are registered trademarks of
- * Adobe Systems Incorporated.
- *
- * (f) THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO
- * CHANGE WITHOUT NOTICE, AND SHOULD NOT BE CONSTRUED
- * AS A COMMITMENT BY ADOBE SYSTEMS INCORPORATED.
- * ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY
- * OR LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO
- * WARRANTY OF ANY KIND (EXPRESS, IMPLIED OR STATUTORY)
- * WITH RESPECT TO THIS INFORMATION, AND EXPRESSLY
- * DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT
- * OF THIRD PARTY RIGHTS.
- */
-
- /*
- * DrawingView.m
- *
- * This view represents the page that the image is drawn onto. It is
- * a subview of the DocView. The DocView is the document view of
- * the ClipView. This view's real size grows with the scale but the
- * bounds always stays the same.
- *
- * An offscreen buffer, bufferId, is used to draw into and then
- * this buffer is composited onscreen. This technique allows for the
- * separation of the static drawing from the temporal drawing (modal
- * loop redrawing and control point display). The static drawing takes
- * place in the buffer while the temporal drawing takes place in this view.
- * The static drawing is stuff that is complex and that will stay around for
- * a while. Saving this drawing in a buffer elimates having to redraw it
- * for something simple like drawing a control point or something.
- * (The bufferId is the content view of a plain window the size of the
- * content view of this view's window.)
- *
- * Version: 2.0
- * Author: Ken Fromm
- * History:
- * 03-17-91 Added this comment and fixed the preview section.
- */
-
- #import "DrawingView.h"
- #import "DocView.h"
- #import "Document.h"
- #import "GraphicImport.h"
- #import "GraphicList.h"
- #import "ImportPanel.h"
- #import "SaveAsPanel.h"
- #import "epsf.h"
-
- #import "DrawingViewWraps.h"
-
- #import <appkit/Cell.h>
- #import <appkit/ClipView.h>
- #import <appkit/NXCursor.h>
- #import <appkit/NXImage.h>
- #import <appkit/NXBitmapImageRep.h>
- #import <appkit/Text.h>
- #import <appkit/Pasteboard.h>
- #import <appkit/PrintInfo.h>
- #import <appkit/nextstd.h>
-
- #import <dpsclient/dpsclient.h>
- #import <dpsclient/wraps.h>
- #import <math.h>
-
- extern char ControlFont[ ];
-
- extern const NXRect DefaultContentRect;
-
- static char EpsfPboard[] = "Epsf Pasteboard Type";
-
- static char EpsfProcSet[] = "EPSF_Illustrator_abbrev 0 0";
-
- /*
- * Timers used to automatically scroll when the mouse is
- * outside the drawing view and not moving.
- */
- static void startTimer(NXTrackingTimer ** timer, int *timermask, id window)
- {
- if (!*timer)
- {
- *timer = NXBeginTimer(NULL, 0.15, 0.2);
- *timermask = NX_TIMERMASK;
- [window addToEventMask:NX_TIMERMASK];
- }
- }
-
- static void stopTimer(NXTrackingTimer **timer, int *timermask, id window)
- {
- if (*timer)
- {
- NXEndTimer(*timer);
- *timer = NULL;
- *timermask = 0;
- [window removeFromEventMask:NX_TIMERMASK];
- }
- }
-
- void compositeBuffer(int gstate, const NXRect *srce, const NXPoint *dest, int op)
- {
- PScomposite(NX_X(srce), NX_Y(srce), NX_WIDTH(srce), NX_HEIGHT(srce),
- gstate, dest->x, dest->y, op);
- }
-
- /*
- * The next two procedures are for the drawing buffer.
- * This buffer is used to prevent unnecessary drawing by
- * retaining images within offscreen windows.
- *
- * Create a plain window the size of the rectangle passed in and
- * then insert a view into the window as a subview. A clip view
- * is swapped for the content view so the buffer can be scrolled
- * when this view is scrolled.
- */
- static id createBuffer(const NXSize *winSize)
- {
- id buffer, clipview, window;
-
- NXRect contRect;
-
- contRect.origin.x = contRect.origin.y = 0;
- contRect.size = *winSize;
- window = [Window newContent:&contRect
- style:NX_PLAINSTYLE
- backing:NX_RETAINED
- buttonMask:0
- defer:NO] ;
-
- buffer = [[[[View newFrame:&contRect] allocateGState] setFlipped:NO] setClipping:NO];
- clipview = [[[ClipView new] setFlipped:NO] setDisplayOnScroll:NO];
- [[window setContentView:clipview ] free];
- [clipview setDocView:buffer];
- [clipview allocateGState];
-
- [window display];
-
- return buffer;
- }
-
- /*
- * Resize the buffer and its window. Called when this view's
- * window resizes. (The buffer is enlarged by a bit as an
- * added insurance.)
- */
- void resizeBuffer(id buffer, const NXSize*newSize)
- {
- NXRect frameRect, contRect;
-
- [buffer getFrame:&frameRect];
- if (newSize->width > frameRect.size.width || newSize->height > frameRect.size.height)
- {
- contRect.origin.x = contRect.origin.y = 0.0;
- contRect.size = *newSize;
- NXInsetRect(&contRect, -10.0, -10.0);
- [Window getFrameRect:&frameRect
- forContentRect:&contRect
- style:NX_PLAINSTYLE];
- [[buffer window] sizeWindow:frameRect.size.width :frameRect.size.height];
- [buffer sizeTo:newSize->width :newSize->height];
- }
- }
-
- @implementation DrawingView
-
- +newFrame:(const NXRect *) frameRect
- {
- self = [super newFrame:frameRect];
- [[self allocateGState] setClipping:NO];
-
- graphiclistId = [GraphicList new];
- selectedlistId = [GraphicList new];
-
- hitPoint = [NXApp hitPoint];
- upathBuffer = [NXApp upathBuffer];
-
- bufferId = createBuffer(&DefaultContentRect.size);
-
- return self;
- }
-
- /* Free any unplaced imported object. */
- - free
- {
- [graphicId free];
- [[graphiclistId freeObjects] free];
- [selectedlistId free];
-
- [[bufferId window] free];
-
- return [super free];
- }
-
- /*
- * Aids for understanding the operation of the application.
- * Moves the offscreen buffer onscreen.
- */
- - showBuffer:sender
- {
- [[bufferId window] moveTo:10 :10];
- [[bufferId window] orderFront:self];
-
- [window orderFront:self];
-
- return self;
- }
-
- - hideBuffer:sender
- {
- [[bufferId window] orderOut:self];
-
- return self;
- }
-
- /*
- * Returns the size of the control point scaled to reflect the current scale.
- */
- - (float) controlPointSize
- {
- return FONTSIZE * (1.0/[superview scale]);
- }
-
- /*
- * Scale the hit setting. Using an unscaled hit setting would be like
- * using a boxing glove on a 400% scale.
- */
- - (float) hitSetting
- {
- return ([NXApp hitSetting] * (1.0/[superview scale]));
- }
-
- - buffer
- {
- return bufferId;
- }
-
- - image
- {
- return imageId;
- }
-
- - setDirty:(BOOL) flag
- {
- dirty = flag;
- [window setDocEdited:flag];
-
- return self;
- }
-
- - (BOOL)isDirty
- {
- return dirty;
- }
-
- - (BOOL)isEmpty
- {
- return [graphiclistId count] == 0;
- }
-
- - (BOOL)isSelected
- {
- return [selectedlistId count] > 0;
- }
-
- /*
- * Set the cursor to be the intersection of the bounds
- * and the visible portion of this view.
- */
- - resetCursorRects
- {
- NXRect visRect;
-
- [self getVisibleRect:&visRect];
- NXIntersectionRect(&bounds, &visRect);
- [self addCursorRect:&visRect cursor:[NXApp cursor]];
-
- return self;
- }
-
- /*
- * When the drawing view moves, then move bufferId so that
- * the composites from bufferId are taken from the correct spot.
- */
- - moveTo:(NXCoord)x :(NXCoord)y
- {
- [super moveTo:x :y];
- [bufferId moveTo:x :y];
-
- return self;
- }
-
- /*
- * A scale as well needs to be reflected in the offscreen buffer.
- */
- - scale:(NXCoord)x :(NXCoord)y
- {
- [super scale:x :y];
- [bufferId scale:x :y];
-
- return self;
- }
-
- /*
- * This method copies the PostScript code for the graphics and writes it to the
- * stream passed in. Includes the preview image when appropriate.
- */
- - writePSToStream:(NXStream *) stream
- {
- id nximageId, templist;
-
- NXRect bbox;
-
- if (stream && [selectedlistId count])
- {
- nximageId = NULL;
- imageId = NULL;
- templist = graphiclistId;
- graphiclistId = selectedlistId;
- [graphiclistId getBounds:&bbox];
- if ([[SaveAsPanel new] format] == SAVE_EPSPREVIEW)
- {
- nximageId = [[NXImage alloc] initSize:&bbox.size];
- [nximageId useCacheWithDepth:NX_TwoBitGrayDepth];
- if ([nximageId lockFocus])
- {
- PStranslate(-bbox.origin.x, -bbox.origin.y);
- PSsetgray(NX_WHITE);
- NXRectFill(&bbox);
- [graphiclistId drawObject:&bbox withFlags:NOFLAGS inView:self];
-
- imageId = [[NXBitmapImageRep alloc] initData:NULL fromRect:&bbox];
- [nximageId unlockFocus];
- }
- }
-
- [self copyPSCodeInside:&bbox to:stream];
-
- selectedlistId = graphiclistId;
- graphiclistId = templist;
- [nximageId free];
- [imageId free];
- imageId = NULL;
- }
-
- return self;
- }
-
- /* Pasteboard-related target/action methods */
- /* Calls copy:, then removes and frees the objects in the selection. */
- - cut:sender
- {
- float knobsize;
-
- NXRect rect;
-
- if ([selectedlistId count])
- {
- knobsize = -[self controlPointSize]/2;
- [selectedlistId getBounds:&rect];
- NXInsetRect(&rect, knobsize, knobsize);
-
- if (sender != self)
- [self copy:sender];
- [graphiclistId removeObjectsIn:selectedlistId];
- [selectedlistId freeObjects];
-
- [self display:&rect :1];
- [self setDirty:YES];
-
- return self;
- }
-
- return nil;
- }
-
- /* Calls cut bypassing the copy. */
- - delete:sender
- {
- return [self cut:self];
- }
-
- /*
- * Puts all the objects in the selected list into the Pasteboard by
- * archiving the list. See the draw program for placing the objects
- * in the Pasteboard as a PostScript image. Much of the work is
- * available already. The right hooks just have to be added.
- */
- - copy:sender
- {
- BOOL error = YES;
-
- id pasteboardId = [Pasteboard new];
-
- char *dataptr;
-
- const char *types[1];
-
- int length, maxlen;
-
- NXStream *stream;
-
- NXTypedStream *ts;
-
- if ([selectedlistId count])
- {
- types[0] = EpsfPboard;
-
- stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
- if (stream)
- {
- ts = NXOpenTypedStream(stream, NX_WRITEONLY);
- if (ts)
- {
- NXWriteRootObject(ts, selectedlistId);
- NXCloseTypedStream(ts);
- NXGetMemoryBuffer(stream, &dataptr, &length, &maxlen);
-
- [pasteboardId declareTypes:types num:2 owner:[self class]];
- [pasteboardId writeType:EpsfPboard data:dataptr length:length];
- error = NO;
- }
- NXCloseMemory(stream, NX_FREEBUFFER);
- }
- }
-
- return (error ? nil : self);
- }
-
- /* Routines to check the types in the Pasteboard */
- static BOOL matchPasteType(char *const *types, const char *type)
- {
- if (types)
- while (*types)
- if (!strcmp(*types++, type))
- return YES;
-
- return NO;
- }
-
- /*
- * Validates the pasteboard types and returns the preferred one.
- */
- static const char *drawPasteType(char *const *types)
- {
- if (matchPasteType(types, EpsfPboard))
- return EpsfPboard;
- if (matchPasteType(types, NXPostScriptPboard))
- return NXPostScriptPboard;
- if (matchPasteType(types, NXTIFFPboardType))
- return NXTIFFPboardType;
-
- return NULL;
- }
-
- /*
- * Pastes any type available from the Pasteboard into the DrawingView.
- * If the type in the Pasteboard is the internal type, then the objects
- * are simply added to the selected list and graphic list. If it is of PostScript
- * or Tiff type then create a GraphicImport object using the contents of the
- * Pasteboard.
- */
- - paste:sender
- {
- id objectId,
- pastelistId,
- pasteboardId = [Pasteboard new];
-
- char *dataptr;
-
- const char *type;
-
- int length;
-
- float knobsize;
-
- NXPoint offset;
-
- NXRect drawRect, visRect;
-
- NXStream *stream;
-
- NXTypedStream *ts;
-
- type = drawPasteType([pasteboardId types]);
- if (type)
- {
- [self lockFocus];
- [self deselectObject:selectedlistId];
- [self unlockFocus];
- [pasteboardId readType:type data:&dataptr length:&length];
- stream = NXOpenMemory(dataptr, length, NX_READONLY);
- if (strcmp(type, EpsfPboard) == 0)
- {
- ts = NXOpenTypedStream(stream, NX_READONLY);
- pastelistId = NXReadObject(ts);
- if ([pastelistId count])
- {
- [graphiclistId insertObjectsIn:pastelistId];
- [selectedlistId insertObjectsIn:pastelistId];
- [selectedlistId setSelected:YES];
- }
- NXCloseTypedStream(ts);
- }
- else if (strcmp(type, NXPostScriptPboard) == 0 ||
- strcmp(type, NXTIFFPboardType) == 0) {
- objectId = [[GraphicImport alloc] initFromStream:stream];
- [graphiclistId addObject:objectId];
- [selectedlistId addObject:objectId];
- [selectedlistId setSelected:YES];
- }
- NXCloseMemory(stream, NX_SAVEBUFFER);
-
- if ([selectedlistId count])
- {
- knobsize = -[self controlPointSize]/2;
- [selectedlistId getBounds:&drawRect];
- NXInsetRect(&drawRect, knobsize, knobsize);
-
- [self getVisibleRect:&visRect];
- offset.x = (visRect.origin.x + visRect.size.width/2.0) -
- (drawRect.origin.x + drawRect.size.width/2.0);
- offset.y = (visRect.origin.y + visRect.size.height/2.0) -
- (drawRect.origin.y + drawRect.size.height/2.0);
-
- [selectedlistId moveAll:&offset];
- drawRect.origin.x += offset.x;
- drawRect.origin.y += offset.y;
- [self display:&drawRect :1];
- [self setDirty:YES];
- }
- }
- else
- return nil;
-
- return self;
- }
-
- /*
- * Selects all the items in the graphiclistId.
- */
- - selectAll:sender
- {
- int i;
-
- float knobsize;
-
- NXRect rect;
-
- i = [graphiclistId count];
- if (i && i > [selectedlistId count])
- {
- [selectedlistId free];
- selectedlistId = [graphiclistId copy];
- [selectedlistId setSelected:YES];
-
- knobsize = -[self controlPointSize]/2;
- [selectedlistId getBounds:&rect];
- NXInsetRect(&rect, knobsize, knobsize);
-
- [self lockFocus];
- [self drawControl:selectedlistId forRect:&rect withFlags:NOFLAGS];
- [self unlockFocus];
- [window flushWindow];
- }
-
- return self;
- }
-
- /*
- * Brings each of the items in the selected list to the front of the
- * graphics list. The first item selected will be at the front of the
- * graphics list with the others following immediately behind.
- */
- - bringToFront:sender
- {
- id object;
-
- BOOL change = NO;
-
- int i, j, index;
-
- float knobsize;
-
- NXRect rect;
-
- j = [selectedlistId count];
- for (i = 0; i < j; i++)
- {
- object = [selectedlistId objectAt:i];
- index = [graphiclistId indexOf:object];
- if (index != i)
- {
- [graphiclistId removeObjectAt:index];
- [graphiclistId insertObject:object at:i];
- change = YES;
- }
- }
-
- if (change)
- {
- knobsize = -[self controlPointSize]/2;
- [selectedlistId getBounds:&rect];
- NXInsetRect(&rect, knobsize, knobsize);
-
- [self display:&rect :1];
- [self setDirty:YES];
- }
-
- return self;
- }
-
- /*
- * Moves each of the items in the selected list to the back of the
- * graphics list. The last item selected will be at the back of the
- * graphics list with the others following immediately ahead.
- */
- - sendToBack:sender
- {
- id object;
-
- BOOL change = NO;
-
- int numS, numG, i, index;
-
- float knobsize;
-
- NXRect rect;
-
- numS = [selectedlistId count] - 1;
- numG = [graphiclistId count] - 1;
- for (i = numS; i >= 0; i--)
- {
- object = [selectedlistId objectAt:i];
- index = [graphiclistId indexOf:object];
- if (numG - index != numS - i)
- {
- [graphiclistId removeObjectAt:index];
- [graphiclistId insertObject:object at:(numG - (numS - i))];
- change = YES;
- }
- }
-
- if (change)
- {
- knobsize = -[self controlPointSize]/2;
- [selectedlistId getBounds:&rect];
- NXInsetRect(&rect, knobsize, knobsize);
-
- [self display:&rect :1];
- [self setDirty:YES];
- }
-
- return self;
- }
-
- - makeOriginalUsing:(SEL) aMethod
- {
- float knobsize;
-
- NXRect rect_start, rect_end;
-
- knobsize = -[self controlPointSize]/2;
- [selectedlistId getBounds:&rect_start];
- NXInsetRect(&rect_start, knobsize, knobsize);
-
- if ([selectedlistId makeObjectsPerform:aMethod])
- {
- [selectedlistId getBounds:&rect_end];
- NXInsetRect(&rect_end, knobsize, knobsize);
-
- NXUnionRect(&rect_start, &rect_end);
- [self lockFocus];
- [self drawSelf:&rect_end :1];
- [self unlockFocus];
- [self setDirty:YES];
- [window flushWindow];
- }
-
- return self;
- }
-
- /*
- * Sizes the selected objects to have their original sizes.
- */
- - originalSize:sender
- {
- return [self makeOriginalUsing:@selector(setOriginalSize)];
- }
-
- /*
- * Sizes the selected objects to have the save width/height ratio as their
- * original bounding boxes. Centers them about their centers.
- */
- - originalRatio:sender
- {
- return [self makeOriginalUsing:@selector(setOriginalRatio)];
- }
-
- /*
- * Scrolls to rectangle passed in if it is not in visible portion of the view.
- * If the rectangle is larger in width or height than the view, the scrollRectToVisible
- * method is not altogether consistent. As a result, the rectangle contains only
- * the image that was previously visible.
- */
- - scrollToRect:(const NXRect *)toRect
- {
- NXRect visRect;
-
- [self getVisibleRect:&visRect];
- if (!NXContainsRect(&visRect, toRect))
- {
- [window disableFlushWindow];
- [self scrollRectToVisible:toRect];
- [window reenableFlushWindow];
-
- startTimer(&timer, &timermask, window);
- }
- else
- stopTimer(&timer, &timermask, window);
-
- return self;
- }
-
- /*
- * Constrain the point within the view. An offset is needed because when
- * an object is moved, it is often grabbed in the center of the object. If the
- * lower left offset and the upper right offset were not included then part of
- * the object could be moved off of the view. (In some applications, that might
- * be allowed but in this one the object is constrained to always lie in the
- * page.)
- */
- - constrainPoint:(NXPoint *)aPt withOffset:(const NXSize*)llOffset :(const NXSize*)urOffset
- {
- float margin;
-
- NXPoint viewMin, viewMax;
-
- margin = ceil(FONTSIZE/2);
-
- viewMin.x = bounds.origin.x + llOffset->width + margin;
- viewMin.y = bounds.origin.y + llOffset->height + margin;
-
- viewMax.x = bounds.origin.x + bounds.size.width - urOffset->width - margin;
- viewMax.y = bounds.origin.y + bounds.size.height - urOffset->height - margin;
-
- aPt->x = MAX(viewMin.x, aPt->x);
- aPt->y = MAX(viewMin.y, aPt->y);
-
- aPt->x = MIN(viewMax.x, aPt->x);
- aPt->y = MIN(viewMax.y, aPt->y);
-
- return self;
- }
-
- /*
- * Constrain a rectangle within the view.
- */
- - constrainRect:(NXRect *)aRect
- {
- float margin;
-
- NXPoint viewMin, viewMax;
-
- margin = ceil(FONTSIZE/2);
-
- viewMin.x = bounds.origin.x + margin;
- viewMin.y = bounds.origin.y + margin;
-
- viewMax.x = bounds.origin.x + bounds.size.width - aRect->size.width - margin;
- viewMax.y = bounds.origin.y + bounds.size.height - aRect->size.height - margin;
-
- aRect->origin.x = MAX(viewMin.x, aRect->origin.x);
- aRect->origin.y = MAX(viewMin.y, aRect->origin.y);
-
- aRect->origin.x = MIN(viewMax.x, aRect->origin.x );
- aRect->origin.y = MIN(viewMax.y, aRect->origin.y);
-
- return self;
- }
-
- /*
- * Redraws the graphic. The image from the buffer is composited
- * into the window and then the changed object is drawn atop the
- * old image. A copy of the image is necessary because when the
- * window is scrolled the buffer is also scrolled. When the
- * buffer is scrolled, the old image might have to be redrawn.
- * As a result, a copy is created and the changes performed on the
- * copy. Care is taken to limit the amount of area that must be
- * composited and redrawn. A timer is started is the scrolling rect
- * moves outside the visible portion of the view.
- */
- - redrawObject:objectId :(int) pt_num
- {
- id copyId;
-
- BOOL tracking,
- dirtyFlag = NO;
-
- int old_mask;
-
- float knobsize;
-
- NXPoint pt, pt_last, pt_old, delta;
-
- NXRect rect_now, rect_start, rect_last, rect_scroll, rect_vis;
-
- NXEvent *event;
-
- timermask = 0;
-
- /*
- * Create a copy of the selected object. If we scroll we will need to redraw
- * the old object. If we do not create a copy we will not have an old object.
- * The copy method copies the stream pointer and not the data
- * if the imported file happens to be copied into the document.
- */
- copyId = [objectId copyTemp];
-
- knobsize = -[self controlPointSize]/2;
- [copyId getBounds:&rect_start];
- NXInsetRect(&rect_start, knobsize, knobsize);
-
- rect_now = rect_last = rect_start;
-
- /*
- * The rect_scroll will cause scrolling whenever it goes outside the
- * visible portion of the view.
- */
- [copyId getScrollRect:&rect_scroll forPtNum:pt_num];
- NXInsetRect(&rect_scroll, knobsize, knobsize);
-
- [self getVisibleRect:&rect_vis];
- NXIntersectionRect(&rect_vis, &rect_scroll);
-
- [copyId getPoint:pt_num :&pt_last];
- pt_old = pt_last;
-
- old_mask = [window addToEventMask:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
- event = [NXApp getNextEvent:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
- if (event->type != NX_MOUSEUP)
- {
- tracking = YES;
- while (tracking)
- {
- /*
- * If its a timer event than use the last point. It will be converted to
- * the view's coordinate so it will appear as a new point.
- */
- if (event->type == NX_TIMER)
- pt = pt_old;
- else
- pt = pt_old = event->location;
-
- [self convertPoint:&pt fromView:nil];
- [copyId constrainPoint:&pt forPtNum:&pt_num
- inRect:&bounds withFlags:event->flags];
-
- delta.x = pt.x - pt_last.x;
- delta.y = pt.y - pt_last.y;
-
- if (delta.x || delta.y)
- {
- dirtyFlag = YES;
- /* Change the point location and get the new bounds. */
- [copyId setPoint:pt_num :&delta];
- [copyId getBounds:&rect_now];
- NXInsetRect(&rect_now, knobsize, knobsize);
-
- /* Change the scrolling rectangle. */
- NXOffsetRect(&rect_scroll, delta.x, delta.y);
- [self scrollToRect:&rect_scroll];
-
- /* Composite the old image and then redraw the new one. */
- compositeBuffer([bufferId gState], &rect_last, &rect_last.origin, NX_COPY);
- [self drawObject:copyId forRect:&rect_now withFlags:REDRAWFLAG];
- [self drawControl:copyId forRect:&rect_now withFlags:NOFLAGS];
-
- /* Sync up the drawing so it proceeds a little smoother. */
- [window flushWindow];
- NXPing();
-
- rect_last = rect_now;
- pt_last = pt;
- }
- else
- stopTimer(&timer, &timermask, window);
-
- event = [NXApp getNextEvent:NX_MOUSEUPMASK|
- NX_MOUSEDRAGGEDMASK|timermask];
- tracking = (event->type != NX_MOUSEUP);
- }
- stopTimer(&timer, &timermask, window);
- }
- [window setEventMask:old_mask];
-
- if (![graphiclistId replaceObject:objectId with:copyId])
- [graphiclistId insertObject:copyId at:0];
-
- if (![selectedlistId replaceObject:objectId with:copyId])
- [selectedlistId insertObject:copyId at:0];
-
- [objectId freeTemp];
-
- if (dirtyFlag)
- {
- /*
- * The view has already been focused and we know what
- * has to be redrawn so call drawSelf:: instead of display
- */
- NXUnionRect(&rect_last, &rect_start);
- [self drawSelf:&rect_start :1];
- [window flushWindow];
- NXPing();
- [self setDirty:YES];
- }
-
- return self;
- }
-
- /*
- * Moves the selected objects by performing a translate before drawing
- * the objects. This approach is used because the objects are drawn
- * into windows and drawing simply means compositing the windows.
- *
- * The offsets constrain the selected object to stay within the dimensions
- * of the view.
- */
- - moveObject:(NXEvent *)event
- {
- BOOL tracking,
- moveFlag = NO,
- dirtyFlag = NO;
-
- int old_mask;
-
- long time;
-
- float knobsize;
-
- NXSize llOffset, urOffset;
-
- NXPoint pt, pt_last, pt_old, delta, delta_scroll;
-
- NXRect rect_now, rect_start, rect_last, rect_scroll, rect_vis;
-
- /*
- * Get the scrolling rectangle. Compare it against a reduced version of
- * the visible rect. If it turns out to be the larger then use the reduced
- * rectangle so that the user is not playing pong when trying to
- * move the image.
- */
- knobsize = -[self controlPointSize]/2;
- [selectedlistId getScrollRect:&rect_scroll forPtNum:-1];
- NXInsetRect(&rect_scroll, knobsize, knobsize);
-
- [self getVisibleRect:&rect_vis];
- NXInsetRect(&rect_vis, rect_vis.size.width * 0.20, rect_vis.size.height * 0.20);
- if (rect_scroll.size.width > rect_vis.size.width || rect_scroll.size.height > rect_vis.size.height)
- {
- NXInsetRect(&rect_scroll, MAX(0, (rect_scroll.size.width - rect_vis.size.width)/2),
- MAX(0, (rect_scroll.size.height - rect_vis.size.height)/2));
- }
-
- [selectedlistId getBounds:&rect_start];
- NXInsetRect(&rect_start, knobsize, knobsize);
- rect_now = rect_last = rect_start;
-
- delta_scroll.x = rect_scroll.origin.x - rect_now.origin.x;
- delta_scroll.y = rect_scroll.origin.y - rect_now.origin.y;
-
- timermask = 0;
- time = event->time;
- pt_last = pt_old = event->location;
- [self convertPoint:&pt_last fromView:nil];
-
- /* Calculate where the mouse point falls relative to the object. */
- llOffset.width = pt_last.x - rect_start.origin.x;
- llOffset.height = pt_last.y - rect_start.origin.y;
- urOffset.width = rect_start.origin.x + rect_start.size.width - pt_last.x;
- urOffset.height = rect_start.origin.y + rect_start.size.height - pt_last.y;
-
- /* Return nil if the the mouse was not dragged. */
- old_mask = [window addToEventMask:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
- event = [NXApp getNextEvent:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
- if (event->type != NX_MOUSEUP)
- {
- tracking = YES;
- while (tracking)
- {
- /*
- * Only move the object if a certain amount of time has elapsed
- * between mouse down and mouse up. Prevents accidently
- * moving objects when selecting.
- */
- if (event->time - time > MOVE_INTERVAL)
- {
- moveFlag = YES;
- if (event->type == NX_TIMER)
- pt = pt_old;
- else
- pt = pt_old = event->location;
-
- [self convertPoint:&pt fromView:nil];
- [self constrainPoint:&pt withOffset:&llOffset :&urOffset];
- [self constrainPoint:&pt_last withOffset:&llOffset :&urOffset];
- delta.x = pt.x - pt_last.x;
- delta.y = pt.y - pt_last.y;
-
- if (delta.x || delta.y)
- {
- dirtyFlag = YES;
- NXOffsetRect(&rect_now, delta.x, delta.y);
- [self constrainRect:&rect_now];
-
- rect_scroll.origin.x = rect_now.origin.x + delta_scroll.x;
- rect_scroll.origin.y = rect_now.origin.y + delta_scroll.y;
- [self scrollToRect:&rect_scroll];
-
- /*
- * Composite the old image into the window and then
- * translate the user space and draw the objects.
- */
- compositeBuffer([bufferId gState], &rect_last, &rect_last.origin, NX_COPY);
- PSgsave();
- PStranslate(rect_now.origin.x - rect_start.origin.x,
- rect_now.origin.y - rect_start.origin.y);
- [self drawObject:selectedlistId forRect:&rect_start withFlags:MOVEFLAG];
- [self drawControl:selectedlistId forRect:&rect_start
- withFlags:NOFLAGS];
- PSgrestore();
-
- [window flushWindow];
- NXPing();
-
- rect_last = rect_now;
- pt_last = pt;
- }
- else
- stopTimer(&timer, &timermask, window);
- }
- event = [NXApp getNextEvent:NX_MOUSEUPMASK|
- NX_MOUSEDRAGGEDMASK|timermask];
-
- tracking = (event->type != NX_MOUSEUP);
- }
- stopTimer(&timer, &timermask, window);
-
- if (dirtyFlag)
- {
- delta.x = rect_now.origin.x - rect_start.origin.x;
- delta.y = rect_now.origin.y - rect_start.origin.y;
- [selectedlistId moveAll:&delta];
-
- /*
- * The view has already been focused and we know what
- * has to be redrawn so call drawSelf:: instead of display
- */
- NXUnionRect(&rect_now, &rect_start);
- [self drawSelf:&rect_start :1];
- [window flushWindow];
- NXPing();
- [self setDirty:YES];
- }
- }
-
- [window setEventMask:old_mask];
-
- /*
- * Return nil if the elapsed time is less than the amount necessary to
- * consider the action a move.
- */
- if (moveFlag)
- return self;
- else
- return nil;
- }
-
- -eraseRotatePoint:(NXRect *) drawnRect
- {
- float scale;
-
- NXRect rotateRect;
-
- scale = 1.0 / [superview scale];
- rotateRect.origin.x = rotatePoint.x - 8.0 * scale;
- rotateRect.origin.y = rotatePoint.y - 8.0 * scale;
- rotateRect.size.width = rotateRect.size.height = 16.0 * scale;
- if (!NXContainsRect(drawnRect, &rotateRect))
- {
- compositeBuffer([bufferId gState], &rotateRect, &rotateRect.origin, NX_COPY);
- [self drawControl:selectedlistId forRect:&rotateRect withFlags:NOFLAGS];
- }
-
- return self;
- }
-
- -drawRotatePoint
- {
- float scale;
-
- NXPoint compPt;
-
- scale = 1.0 / [superview scale];
- compPt.x = rotatePoint.x - 8.0 * scale;
- compPt.y = rotatePoint.y - 8.0 * scale;
- [[[NXApp getCursor:OP_ROTATE1] image] composite:NX_SOVER toPoint:&compPt];
-
- return self;
- }
-
- /*
- * Mark the point about which to rotate. Employ gravity near any
- * control points.
- */
- - rotateObjectStart:(NXEvent *)event
- {
- id objectId;
-
- int pt_num;
-
- rotatePoint = event->location;
- [self convertPoint:&rotatePoint fromView:nil];
- [self lockFocus];
- if (objectId = [self checkControl:&rotatePoint :&pt_num])
- [objectId getPoint:pt_num :&rotatePoint];
- [self drawRotatePoint];
- [self unlockFocus];
- [window flushWindow];
-
- return self;
- }
-
- /*
- * Rotates the selected graphics about the rotatePoint selected
- * from the previous mouse down. The image from the buffer
- * is composited into the window and then the rotated object is
- * drawn atop the old image.
- *
- * Care is taken to limit the amount of area that must be
- * composited and redrawn. A timer is started is the scrolling rect
- * moves outside the visible portion of the view.
- */
- - rotateObject:(NXEvent *)event
- {
- id slist;
-
- BOOL tracking,
- dirtyFlag = NO;
-
- int old_mask;
-
- float knobsize, marginsize;
-
- float radians_start, radians_last, radians_delta;
-
- NXPoint pt, pt_old, d;
-
- NXRect rect_now, rect_start, rect_last, rect_scroll;
-
- slist = [selectedlistId copyTemp];
-
- timermask = 0;
- marginsize = (1.0/[superview scale] * SCROLL_MARGIN)/2;
- rect_scroll.size.width = rect_scroll.size.height = marginsize * 2;
-
- knobsize = -[self controlPointSize]/2;
- [slist getBounds:&rect_start];
- NXInsetRect(&rect_start, knobsize, knobsize);
-
- rect_now = rect_last = rect_start;
-
- pt_old = pt = event->location;
- [self convertPoint:&pt fromView:nil];
-
- d.x = pt.x - rotatePoint.x;
- d.y = pt.y - rotatePoint.y;
-
- radians_start = 0;
- if (d.x || d.y)
- radians_start = atan2(d.y,d.x);
- radians_last = radians_start;
-
- [self lockFocus];
- old_mask = [window addToEventMask:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
- event = [NXApp getNextEvent:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
- if (event->type != NX_MOUSEUP)
- {
- tracking = YES;
- while (tracking)
- {
- /*
- * If its a timer event than use the last point. It will be converted to
- * the view's coordinate so it will appear as a new point.
- */
- if (event->type == NX_TIMER)
- pt = pt_old;
- else
- pt = pt_old = event->location;
-
- [self convertPoint:&pt fromView:nil];
-
- d.x = pt.x - rotatePoint.x;
- d.y = pt.y - rotatePoint.y;
- if (d.x || d.y)
- {
- dirtyFlag = YES;
- radians_delta = atan2(d.y, d.x) - radians_last;
- [slist constrainAngle:&radians_delta withFlags:event->flags];
-
- /* Rotate the object about the given point at the given angle. */
- [slist rotateAboutPoint:&rotatePoint withAngle:radians_delta];
- [slist getBounds:&rect_now];
- NXInsetRect(&rect_now, knobsize, knobsize);
-
- /* Make the scrolling rectangle the mouse point with a little margin. */
- rect_scroll.origin.x = pt.x - marginsize;
- rect_scroll.origin.y = pt.y - marginsize;
- [self scrollToRect:&rect_scroll];
-
- /* Composite the old image and then redraw the new one. */
- NXIntegralRect(&rect_last);
- compositeBuffer([bufferId gState], &rect_last, &rect_last.origin, NX_COPY);
- [self drawObject:slist forRect:&rect_now withFlags:REDRAWFLAG];
- [self drawControl:slist forRect:&rect_now withFlags:NOFLAGS];
- [self drawRotatePoint];
-
- /* Sync up the drawing so it proceeds a little smoother. */
- [window flushWindow];
- NXPing();
-
- rect_last = rect_now;
- radians_last = radians_delta + radians_last;
- }
- else
- stopTimer(&timer, &timermask, window);
-
- event = [NXApp getNextEvent:NX_MOUSEUPMASK|
- NX_MOUSEDRAGGEDMASK|timermask];
- tracking = (event->type != NX_MOUSEUP);
- }
- stopTimer(&timer, &timermask, window);
- }
- [window setEventMask:old_mask];
-
- [graphiclistId replaceObjectsIn:selectedlistId with:slist];
-
- [selectedlistId freeTemp];
- [selectedlistId free];
- selectedlistId = slist;
-
- if (dirtyFlag)
- {
- /*
- * The view has already been focused and we know what
- * has to be redrawn so call drawSelf:: instead of display
- */
- NXUnionRect(&rect_last, &rect_start);
- [self drawSelf:&rect_start :1];
- [self eraseRotatePoint:&rect_start];
- [window flushWindow];
- NXPing();
- [self setDirty:YES];
- }
- [self unlockFocus];
-
- return self;
- }
-
- /* Check to see whether a control point has been hit. */
- - checkControl:(const NXPoint *) p :(int *) pt_num
- {
- float hitsetting;
-
- NXRect hitRect;
-
- hitsetting = [self hitSetting];
- NXSetRect(&hitRect, p->x - hitsetting/2, p->y - hitsetting/2, hitsetting, hitsetting);
-
- return [selectedlistId hitControl:&hitRect :pt_num forSize:[self controlPointSize]];
- }
-
- /*
- * Check to see whether an object has been hit. Return the selected list if
- * any object in it has been hit. Otherwise just return the individual
- * graphic.
- */
- - checkObject:(const NXPoint *) p in:listId from:(int) start to:(int) end
- {
- id objectId;
-
- BOOL flag;
-
- int i;
-
- float hitsetting;
-
- hitsetting = [self hitSetting];
-
- /* Bounding Box */
- hitPoint->pts[0] = floor(p->x - hitsetting/2);
- hitPoint->pts[1] = floor(p->y - hitsetting/2);
- hitPoint->pts[2] = ceil(p->x + hitsetting/2);
- hitPoint->pts[3] = ceil(p->y + hitsetting/2);
-
- /* Moveto */
- hitPoint->pts[4] = p->x - hitsetting/2;
- hitPoint->pts[5] = p->y - hitsetting/2;
-
- /* Rlineto's */
- hitPoint->pts[7] = hitsetting;
- hitPoint->pts[8] = hitsetting;
- hitPoint->pts[11] = -hitsetting;
-
- flag = (listId == graphiclistId);
- for (i = start; i < end; i++)
- if (objectId = [[listId objectAt:i] hitObject:hitPoint ifNotSelected:flag])
- return objectId;
-
- return nil;
- }
-
- /*
- * Set the object as selected, add the object to the selected list and
- * then draw the control points.
- */
- - selectObject:objectId
- {
- NXRect drawRect;
-
- [objectId setSelected:YES];
- [selectedlistId addObject:objectId];
-
- [self getVisibleRect:&drawRect];
- [self drawControl:objectId forRect:&drawRect withFlags:NOFLAGS];
- [window flushWindow];
-
- return self;
- }
-
- /*
- * Remove the object (or objects) from the selected list and redraw the
- * control points for the visible portion. The control points are drawn
- * in this view since they can be drawn pretty quickly (and so the don't
- * need to be cached in an offscreen buffer somewhere).
- */
- - deselectObject:objectId
- {
- NXRect drawRect;
-
- [self getVisibleRect:&drawRect];
-
- [objectId setSelected:NO];
- if (objectId == selectedlistId)
- [selectedlistId empty];
- else
- [selectedlistId removeObject:objectId];
-
- [self drawControl:selectedlistId forRect:&drawRect withFlags:CLEARFLAG];
- [window flushWindow];
-
- return self;
- }
-
- /*
- * Test for a mouse down hit on either the object or the control points.
- * This algorithm looks further into the list in order to check for mouse
- * hits on objects that lie below the currently selected object.
- */
- - testObject:(NXEvent *)event
- {
- id objectId, newobjectId;
-
- int pt_num, list_index;
-
- NXPoint p;
-
- p = event->location;
- [self convertPoint:&p fromView:nil];
- [self lockFocus];
- if (objectId = [self checkControl:&p :&pt_num])
- {
- [self redrawObject:objectId :pt_num];
- }
- else
- {
- if ([selectedlistId count] == 0 ||
- (event->flags & NX_SHIFTMASK) == NX_SHIFTMASK)
- {
- if (objectId = [self checkObject:&p in:selectedlistId
- from:0 to:[selectedlistId count]])
- [self deselectObject:objectId];
- else if (objectId = [self checkObject:&p in:graphiclistId
- from:0 to:[graphiclistId count]])
- [self selectObject:objectId];
- }
- else
- {
- objectId = [self checkObject:&p in:selectedlistId
- from:0 to:[selectedlistId count]];
- if (!objectId || ![self moveObject:event])
- {
- /* Look further into the list first. */
- list_index = 0;
- if (objectId)
- list_index = [graphiclistId indexOf:objectId];
- if ((newobjectId = [self checkObject:&p in:graphiclistId
- from:list_index to:[graphiclistId count]]) ||
- (newobjectId = [self checkObject:&p in:graphiclistId
- from:0 to:list_index]))
- {
- [self deselectObject:selectedlistId];
- [self selectObject:newobjectId];
- }
- else if (!objectId)
- [self deselectObject:selectedlistId];
- }
- }
- }
- [self unlockFocus];
-
- return self;
- }
-
- /*
- * Free any previously unplaced imported object first. Next,
- * pass the file name to the factory Tiff or Epsf object to create a new
- * instance. If successful then remove items from the selected list.
- *
- * If a point has been passed in then place the imported file at its
- * original size with its left corner corresponding to p. If not
- * then set the NXApp operation to OP_PLACE. The next mouse
- * down will begin the modal loop for drawing out the sizing rectangle.
- */
- - importFile:(const char *) file at:(NXPoint *) p
- {
- BOOL ok;
-
- char *end;
-
- NXStream *stream;
-
- ok = NO;
- [graphicId free];
- graphicId = NULL;
- if (file)
- {
- end = strrchr(file, '.');
- if (end)
- {
- if ([[ImportPanel new] format] == IMPORT_COPY)
- {
- stream = NXMapFile(file, NX_READONLY);
- if (stream)
- {
- if (strncmp(end, ".tiff", 5) == 0 ||
- strncmp(end, ".ps", 3) == 0 ||
- strncmp(end, ".eps", 4) == 0)
- {
- graphicId = [[GraphicImport alloc] initFromStream:stream];
- [graphicId setFilename:file];
- }
- NXCloseMemory(stream, NX_SAVEBUFFER);
- }
- else
- Notify("Import Error", "Unable to open file.");
- }
- else
- {
- if (strncmp(end, ".tiff", 5) == 0 ||
- strncmp(end, ".ps", 3) == 0 ||
- strncmp(end, ".eps", 4) == 0)
- {
- graphicId = [[GraphicImport alloc] initFromFile:file];
- }
- }
-
- if (graphicId)
- {
- [self lockFocus];
- [self deselectObject:selectedlistId];
- [self unlockFocus];
-
- if (p)
- {
- [self placeObjectAt:p];
- graphicId = NULL;
- }
- else
- [NXApp setOperation:OP_IMPORT];
-
- [window flushWindow];
- NXPing();
- ok = YES;
- }
- }
- else
- Notify("Import Error", "Unable to import file. Unrecognized file type.");
- }
-
- return ok ? self : nil;
- }
-
- /*
- * Place the graphicId with its upper left corner at p;
- */
- - placeObjectAt:(const NXPoint *) p
- {
- float knobsize;
-
- NXPoint pt;
-
- NXRect rect_draw;
-
- if (graphicId)
- {
- pt = *p;
- [graphiclistId insertObject:graphicId at:0];
- [selectedlistId insertObject:graphicId at:0];
-
- [graphicId getBounds:&rect_draw];
- pt.y = pt.y - rect_draw.size.height;
- [graphicId setOrigin:&pt];
-
- knobsize = -[self controlPointSize]/2;
- [graphicId getBounds:&rect_draw];
- NXInsetRect(&rect_draw, knobsize, knobsize);
-
- [self display:&rect_draw :1];
- }
-
- return self;
- }
-
- /*
- * Begins the setup for placing an imported file into the document.
- * The object for the file is created and then waits for the mouse
- * down to begin placement and sizing. This method first gets the
- * object and then messages the redrawObject method to draw
- * the subsequent sizing rectangles.
- */
- - importObject:(NXEvent *)event
- {
- float knobsize;
-
- NXPoint p;
-
- NXRect rect_draw;
-
- if (graphicId)
- {
- p = event->location;
- [self convertPoint:&p fromView:nil];
- if ([[ImportPanel new] dragToSize])
- {
- [NXApp setOperation:OP_PLACE];
-
- NXSetRect(&rect_draw, p.x, p.y-SIZE_MIN, SIZE_MIN, SIZE_MIN);
- [graphicId setBounds:&rect_draw];
-
- knobsize = -[self controlPointSize]/2;
- NXInsetRect(&rect_draw, knobsize, knobsize);
-
- [self lockFocus];
- [self drawObject:graphicId forRect:&rect_draw withFlags:REDRAWFLAG];
- [self drawControl:graphicId forRect:&rect_draw withFlags:NOFLAGS];
- [self redrawObject:graphicId :8];
- [self unlockFocus];
-
- graphicId = [selectedlistId objectAt:0];
- }
- else
- [self placeObjectAt:&p];
-
- if ([graphicId error])
- {
- [self lockFocus];
- [self deselectObject:graphicId];
- [self unlockFocus];
- [graphiclistId removeObject:graphicId];
- [graphicId free];
- }
- else
- [self setDirty:YES];
- graphicId = NULL;
- }
-
- return self;
- }
-
- /*
- * Depending on the current operation, check for selection, zoom or
- * import a file.
- */
- - mouseDown:(NXEvent *)event
- {
- int operation;
-
- operation = [NXApp operation];
- switch (operation)
- {
- case OP_SELECT:
- [self testObject:event];
- break;
- case OP_ZOOMUP:
- case OP_ZOOMDOWN:
- [nextResponder scaleDrawView:self withEvent:event];
- break;
- case OP_ROTATE1:
- [self rotateObjectStart:event];
- [NXApp setOperation:OP_ROTATE2];
- break;
- case OP_ROTATE2:
- [self rotateObject:event];
- [NXApp setOperation:OP_ROTATE1];
- break;
- case OP_IMPORT:
- [self importObject:event];
- [NXApp clearOperation];
- break;
- }
-
- return self;
- }
-
- /*
- * Changes the cursor to the reduce cursor if the shift key is
- * pressed when zooming.
- */
- - flagsChanged:(NXEvent *) event
- {
- BOOL shift;
-
- int operation;
-
- operation = [NXApp operation];
- shift = (event->flags & NX_ALTERNATEMASK) == NX_ALTERNATEMASK;
-
- if (operation == OP_ZOOMUP && shift)
- [NXApp setOperation:OP_ZOOMDOWN];
- else if (operation == OP_ZOOMDOWN && !shift)
- [NXApp setOperation:OP_ZOOMUP];
- else
- return [super flagsChanged:event];
-
- return self;
- }
-
- /*
- * Deletes the selected objects.
- */
- - keyDown:(NXEvent *) event
- {
- if ([NXApp operation] == OP_SELECT &&
- event->data.key.charSet == NX_ASCIISET &&
- event->data.key.charCode == NX_DELETE)
- return [self cut:self];
- else
- return nil;
- }
-
- /*
- * Draw the control points using the user path buffer to hold
- * the data for the xyshow. Having the object fill in the data
- * structure allows for combining the control points for multiple
- * objects. This increases drawing performance by reducing
- * the number of operations. But since this operation is performed
- * after the objects have been drawn, the control points are above
- * the graphics. This approach may or may not be desired.
- */
- - drawControl:object forRect:(NXRect *)r withFlags:(int)flags
- {
- float knobsize;
-
- NXPoint lastpoint;
-
- NXRect rect;
-
- if (r)
- rect = *r;
- else
- [self getVisibleRect:&rect];
-
- if (flags & CLEARFLAG)
- compositeBuffer([bufferId gState], &rect, &rect.origin, NX_COPY);
-
- lastpoint.x = 0;
- lastpoint.y = 0;
-
- upathBuffer->num_ops = 0;
- upathBuffer->num_pts = 0;
-
- knobsize = [self controlPointSize];
- NXInsetRect(&rect, -knobsize/2, -knobsize/2);
- [object putControlPoints:upathBuffer forRect:&rect :&lastpoint];
-
- upathBuffer->ops[upathBuffer->num_ops] = 0;
- upathBuffer->pts[upathBuffer->num_pts] = 0;
- upathBuffer->pts[upathBuffer->num_pts + 1] = 0;
-
- if (upathBuffer->num_ops > 0)
- {
- PSWSetControlPoints(ControlFont, knobsize, NX_BLACK, 0.15);
- PSWDrawControlPoints(upathBuffer->pts[0], upathBuffer->pts[1],
- &upathBuffer->pts[2], upathBuffer->num_pts, upathBuffer->ops);
- }
-
- return self;
- }
-
- - drawObject:object forRect:(NXRect *)r withFlags:(int) flags
- {
- NXRect rect;
-
- if (r)
- rect = *r;
- else
- [self getVisibleRect:&rect];
-
- [object drawObject:&rect withFlags:flags inView:self];
-
- return self;
- }
-
- /*
- * Fill in the background of the rectangle and then draw the graphics.
- * If NX_DRAWING, then draw into the buffer and composite into
- * this view's window.
- */
- - drawSelf:(NXRect *)r :(int) count
- {
- if (NXDrawingStatus == NX_DRAWING)
- {
- [bufferId lockFocus];
- PSsetgray(NX_WHITE);
- NXRectFill(r);
-
- [bufferId unlockFocus];
- compositeBuffer([bufferId gState], r, &r->origin, NX_COPY);
- [bufferId lockFocus];
- }
-
- [self drawObject:graphiclistId forRect:r withFlags:REFRESHFLAG];
-
- if (NXDrawingStatus == NX_DRAWING)
- {
- [bufferId unlockFocus];
- compositeBuffer([bufferId gState], r, &r->origin, NX_COPY);
- [self drawControl:selectedlistId forRect:r withFlags:NOFLAGS];
- }
-
- return self;
- }
-
- /*
- * This method is only overridden to eliminate during a copy
- * the rectclip and gsave/grestore pairing that results from
- * a lockFocus. These are usually harmless operations but
- * they interfere with trying to produce Illustrator format files.
- */
- - display:(NXRect *)r :(int) count :(BOOL)flag
- {
- if (NXDrawingStatus == NX_COPYING)
- {
- [self drawSelf:r :count];
- DPSFlushContext(DPSGetCurrentContext());
- }
- else
- [super display:r :count :flag];
-
- return self;
- }
-
- /*
- * Do not focus if Printing because we don't want the
- * scale factored in.
- */
- - (BOOL) lockFocus
- {
- if (NXDrawingStatus != NX_PRINTING)
- return [super lockFocus];
-
- return YES;
- }
-
- - unlockFocus
- {
- if (NXDrawingStatus != NX_PRINTING)
- return [super unlockFocus];
-
- return self;
- }
-
- - (BOOL)acceptsFirstResponder
- {
- return YES;
- }
-
- - write:(NXTypedStream *)stream
- {
- [super write:stream];
- NXWriteTypes(stream, "@", &graphiclistId);
-
- return self;
- }
-
- - read:(NXTypedStream *)stream
- {
- [super read:stream];
- NXReadTypes(stream, "@", &graphiclistId);
-
- return self;
- }
-
- - awake
- {
- selectedlistId = [GraphicList new];
-
- hitPoint = [NXApp hitPoint];
- upathBuffer = [NXApp upathBuffer];
-
- bufferId = createBuffer(&DefaultContentRect.size);
-
- return self;
- }
-
- /*
- * Validates menu commands. It returns NO if the
- * DrawingView knows that action is not valid now,
- * otherwise it returns YES.
- *
- * Using pastecount and pastevalid prevents having to look into
- * the Pasteboard every time paste: is validated.
- */
- - (BOOL)validateCommand:menuCell
- {
- SEL action = [menuCell action];
-
- id pasteboardId;
-
- int count;
-
- if ( action == @selector(cut:) ||
- action == @selector(delete:) ||
- action == @selector(copy:) ||
- action == @selector(bringToFront:) ||
- action == @selector(sendToBack:) ||
- action == @selector(originalSize:) ||
- action == @selector(originalRatio:))
- {
- return([selectedlistId count] > 0);
- }
- else if (action == @selector(selectAll:))
- {
- return([graphiclistId count] > 0);
- }
- else if (action == @selector(paste:))
- {
- pasteboardId = [Pasteboard new];
- count = [pasteboardId changeCount];
- if (count != pastecount)
- {
- pastecount = count;
- pastevalid = (drawPasteType([pasteboardId types]) != NULL);
- }
- return pastevalid;
- }
-
- return YES;
- }
-
- - (BOOL) knowsPagesFirst:(int *) firstPageNum last:(int *) lastPageNum
- {
- *firstPageNum = 1;
- *lastPageNum = 1;
-
- return YES;
- }
-
- - (BOOL) getRect:(NXRect *) theRect forPage:(int) page
- {
- if (page == 1)
- {
- *theRect = bounds;
-
- return YES;
- }
-
- return NO;
- }
-
- /*
- * Used when printing. Returns the global resources used in the document.
- */
- - addResources:(Resource *) resourceDoc
- {
- NXAtom string;
-
- if (NXDrawingStatus == NX_COPYING)
- {
- string = NXUniqueString(EpsfProcSet);
- if (!resourceDoc[RES_PROCSETS].states[RES_PRESENT])
- resourceDoc[RES_PROCSETS].states[RES_PRESENT] = [List new];
-
- [resourceDoc[RES_PROCSETS].states[RES_PRESENT]
- addObjectIfAbsent:(id) string];
-
- if (!resourceDoc[RES_PROCSETS].states[RES_SUPPLIED])
- resourceDoc[RES_PROCSETS].states[RES_SUPPLIED] = [List new];
-
- [resourceDoc[RES_PROCSETS].states[RES_SUPPLIED]
- addObjectIfAbsent:(id) string];
- }
-
- return self;
- }
-
- /*
- * Print the %%DocumentResource comments. A list of the resources is
- * accumulated from the imported files (only one in this case).
- * The list is then written to the current context.
- */
- - beginResourceComments:(const NXRect *) bbox
- {
- int i, j;
-
- Resource resourceDoc[RES_NUMTYPES];
-
- bzero(&resourceDoc, sizeof(resourceDoc));
- [self addResources:resourceDoc];
- [graphiclistId addResources:resourceDoc for:(NXRect *) bbox];
-
- for (i = 0; i < RES_NUMTYPES; i++)
- {
- for (j = 0; j < RES_NUMSTATES; j++)
- {
- if (resourceDoc[i].states[j])
- {
- WriteEpsfResource(resourceDoc[i].states[j], i, j);
- [resourceDoc[i].states[j] free];
- }
- }
- }
-
- return self;
- }
-
- /*
- * Write out the necessary information. Overridden to include
- * the fonts from the EPSF files.
- */
- - beginPrologueBBox:(const NXRect *)boundingBox
- creationDate:(const char *)dateCreated
- createdBy:(const char *)anApplication
- fonts:(const char *)fontNames
- forWhom:(const char *)user
- pages:(int)numPages
- title:(const char *)aTitle
- {
- time_t clock;
-
- DPSContext ctxt;
-
- ctxt = DPSGetCurrentContext();
- if (!boundingBox)
- boundingBox = [[NXApp printInfo] paperRect];
-
- if (!dateCreated)
- {
- clock = time(0);
- dateCreated = ctime(&clock);
- }
-
- if (!anApplication)
- anApplication = [NXApp appName];
-
- if (!user)
- user = (char *) getlogin();
-
- if (numPages <= 0)
- numPages = 1;
-
- if (!aTitle)
- aTitle = [[window delegate] filename];
-
- DPSPrintf(ctxt, "%%!PS-Adobe-2.0 EPSF-1.2\n");
- DPSPrintf(ctxt, "%%%%Creator: %s\n", anApplication);
- DPSPrintf(ctxt, "%%%%For: %s\n", user);
- DPSPrintf(ctxt, "%%%%Title: %s\n", aTitle);
- DPSPrintf(ctxt, "%%%%CreationDate: %s", dateCreated);
- DPSPrintf(ctxt, "%%%%BoundingBox: %d %d %d %d\n", (int) floor(boundingBox->origin.x),
- (int) floor(boundingBox->origin.y),
- (int) ceil(boundingBox->origin.x + boundingBox->size.width),
- (int) ceil(boundingBox->origin.y + boundingBox->size.height));
- if (NXDrawingStatus == NX_COPYING)
- DPSPrintf(ctxt, "%%AI3_TemplateBox: %d %d %d %d\n", 306, 396, 306, 396);
- if (NXDrawingStatus != NX_COPYING)
- DPSPrintf(ctxt, "%%%%Pages: %d\n", numPages);
-
- [self beginResourceComments:boundingBox];
-
- if ([[NXApp printInfo] orientation] == NX_LANDSCAPE)
- DPSPrintf(ctxt, "%%%%Orientation: Landscape\n");
- else
- DPSPrintf(ctxt, "%%%%Orientation: Portrait\n");
-
- return self;
- }
-
- /*
- * Includes the abbreviated Illustrator proc set so that
- * the imported files produced through Save To will print on their
- * own. Also includes the preview data as a comment when
- * specified.
- */
- - endHeaderComments
- {
- DPSContext ctxt;
-
- if (NXDrawingStatus == NX_COPYING)
- {
- ctxt = DPSGetCurrentContext();
- DPSPrintf(ctxt, "%%%%EndComments\n\n");
-
- DPSPrintf(ctxt, "%%%%BeginProcSet: EPSF_Illustrator_abbrev 0 0\n");
- WriteEpsfProcSetDef ();
- DPSPrintf(ctxt, "%%%%EndProcSet\n\n");
-
- if (imageId && [[SaveAsPanel new] format] == SAVE_EPSPREVIEW)
- WriteEpsfPreview(imageId);
- }
- else
- [super endHeaderComments];
-
- return self;
- }
-
- /*
- * If saving in illustrator, override the prologue comment.
- */
- - endPrologue
- {
- DPSContext ctxt;
-
- if (NXDrawingStatus != NX_COPYING)
- {
- ctxt = DPSGetCurrentContext();
- DPSPrintf(ctxt, "%%%%EndProlog\n\n");
- }
- else
- [super endPrologue];
-
- return self;
- }
-
- /* Initialize the Illustrator abbreviated proc set. */
- - beginSetup
- {
- DPSContext ctxt;
-
- if (NXDrawingStatus == NX_COPYING)
- {
- ctxt = DPSGetCurrentContext();
- DPSPrintf(ctxt, "%%%%BeginSetup\n");
- WriteEpsfProcSetInit();
- }
- else
- [super beginSetup];
-
- return self;
- }
-
- - endSetup
- {
- DPSContext ctxt;
-
- if (NXDrawingStatus == NX_COPYING)
- {
- ctxt = DPSGetCurrentContext();
- DPSPrintf(ctxt, "%%%%EndSetup\n");
- }
- else
- [super endSetup];
-
- return self;
- }
-
- /* Terminate the Illustrator abbreviated proc set. */
- - beginTrailer
- {
- DPSContext ctxt;
-
- if (NXDrawingStatus == NX_COPYING)
- {
- ctxt = DPSGetCurrentContext();
- DPSPrintf(ctxt, "%%%%Trailer\n");
- WriteEpsfProcSetTerm();
- }
- else
- [super beginTrailer];
-
- return self;
- }
-
- - endTrailer
- {
- if (NXDrawingStatus != NX_COPYING)
- [super endTrailer];
-
- return self;
- }
-
- @end
-